{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[this doc on github](https://github.com/dotnet/interactive/tree/main/samples/notebooks/fsharp/Docs)\n",
    "\n",
    "# Charts with XPlot\n",
    "Charts can be rendered using [Xplot.Plotly](https://fslab.org/XPlot/). \n",
    "We will cover some example on how to use XPlot in a notebook with the .NET Kernel.\n",
    "\n",
    "First, import the `XPlot.Plotly` namespace:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "#i \"nuget:https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet5/nuget/v3/index.json\" \r\n",
    "#i \"nuget:https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-tools/nuget/v3/index.json\" \r\n",
    "\r\n",
    "#r \"nuget: XPlot.Plotly.Interactive, 4.0.6\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "open XPlot.Plotly"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The next cell sets up some helpers for data generation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let generator = new Random()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Rendering Scatter plots\n",
    "One of the most commonly used type of chart to explore data set. Use the type `Scatter`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let openSeries =\n",
    "    Scatter(\n",
    "        name = \"Open\",\n",
    "        x = [1; 2; 3; 4],\n",
    "        y = [10; 15; 13; 17])\n",
    "\n",
    "let closeSeries =\n",
    "    Scatter(\n",
    "        name = \"Close\",\n",
    "        x = [2; 3; 4; 5],\n",
    "        y = [16; 5; 11; 9])\n",
    "\n",
    "[openSeries; closeSeries]\n",
    "|> Chart.Plot\n",
    "|> Chart.WithTitle \"Open vs Close\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's change it to be markers style, so more like a scatter plot."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "openSeries.mode <- \"markers\"\n",
    "closeSeries.mode <- \"markers\"\n",
    "\n",
    "[openSeries; closeSeries]\n",
    "|> Chart.Plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`Scatter` can also produce polar charts by setting the radial property `r` and angular proeprty `t`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let openSeries =\n",
    "    Scatter(\n",
    "        name = \"Open\",\n",
    "        r = [1.; 2.; 3.; 4.],\n",
    "        t = [45.; 100.; 150.; 290.])\n",
    "\n",
    "let closeSeries =\n",
    "    Scatter(\n",
    "        name = \"Close\",\n",
    "        r = [2.; 3.; 4.; 5. ],\n",
    "        t = [16.; 45.; 118.; 90.])\n",
    "\n",
    "[openSeries; closeSeries]\n",
    "|> Chart.Plot\n",
    "|> Chart.WithLayout(Layout(orientation = -90.))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Large scatter plots and performance\n",
    "It is not uncommong to have scatter plots with a large dataset, it is a common scenario at the beginning of a data exploration process. Using the default `svg` based rendering will create performace issues as the dom will become very large.\n",
    "We can then use `web-gl` support to address the problem."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "#!time\n",
    "\n",
    "let series =\n",
    "    [|\n",
    "        for a in 1 .. 10 ->\n",
    "            Scattergl(\n",
    "                name = sprintf \"Series %i\" a, \n",
    "                mode = \"markers\", \n",
    "                x = [ for ax in 1 .. 100000 -> generator.Next(-200, 200) * 1000 * generator.Next(-2000, 2000)],     \n",
    "                y = [ for ay in 1 .. 100000 -> generator.Next(-200, 200) * 1000 * generator.Next(-2000, 2000)])\n",
    "    |]\n",
    "    \n",
    "series\n",
    "|> Chart.Plot\n",
    "|> Chart.WithTitle \"Large Dataset\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Can provide custom marker `colour`, `size` and `colorscale` to display even more information to the user."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let generatePoint () = generator.Next(-200, 200) * 1000 * generator.Next(-2000, 2000)\n",
    "    \n",
    "let sizes =\n",
    "    [ for s in 1..100 ->\n",
    "        if generator.NextDouble() < 0.75 then\n",
    "            generator.Next(1, 5)\n",
    "         else\n",
    "             generator.Next(10, 15) ]\n",
    "    \n",
    "let temperatures = sizes |> Seq.map (fun x -> x * 10 - 100)\n",
    "\n",
    "let series =\n",
    "    [|\n",
    "        for a in 1 .. 10 -> \n",
    "            Scattergl(\n",
    "                name = sprintf \"Series %i\" a,\n",
    "                mode = \"markers\",\n",
    "                x = [ for _ in 1 .. 100 -> generatePoint () ],\n",
    "                y = [ for ay in 1 .. 100 -> generatePoint () ],\n",
    "                marker = \n",
    "                    Marker(\n",
    "                        colorscale = \"hot\",\n",
    "                        color = temperatures,\n",
    "                        size = sizes))\n",
    "    |]\n",
    "    \n",
    "series\n",
    "|> Chart.Plot\n",
    "|> Chart.WithTitle \"Size and Colour\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Color Scales\n",
    "\n",
    "Plotly provides some additional `color scales` to use. Note that we use `display` explicitly to display each graph with separate titles, rather than a single chart."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "for s in series do s.marker.colorscale <- \"Viridis\"\n",
    "\n",
    "series\n",
    "|> Chart.Plot\n",
    "|> Chart.WithTitle \"Viridis scale\"\n",
    "|> display\n",
    "\n",
    "for s in series do s.marker.colorscale <- \"Hot\"\n",
    "\n",
    "series\n",
    "|> Chart.Plot\n",
    "|> Chart.WithTitle \"Hot scale\"\n",
    "|> display\n",
    "\n",
    "for s in series do s.marker.colorscale <- \"Jet\"\n",
    "\n",
    "series\n",
    "|> Chart.Plot\n",
    "|> Chart.WithTitle \"Jet scale\"\n",
    "|> display"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Rendering Histograms\n",
    "Let's have a look at using histograms, the next cell sets up some generators."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let count = 20\n",
    "let dates = [for d in 1 .. count -> DateTime.Now.AddMinutes(float(generator.Next(d, d + 30)))]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's define histogram traces:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let openByTime =\n",
    "    Histogram(\n",
    "        x = dates,\n",
    "        y = [for y in 1 .. count -> generator.Next(0, 200)],\n",
    "        name = \"Open\")\n",
    "    \n",
    "let closeByTime =\n",
    "    Histogram(\n",
    "        x = dates,\n",
    "        y = [for y in 1 .. count -> generator.Next(0, 200)],\n",
    "        name = \"Close\")\n",
    "    \n",
    "[openByTime; closeByTime]\n",
    "|> Chart.Plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Histogram generator will automatically count the number of items per bin. \n",
    "\n",
    "Setting `histfunc` to `\"sum\"` we can now add up all the values contained in each bin.\n",
    "Note that we are creatng bin using the `x` data point and we are using bydefault autobinx"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let openByTime =\n",
    "    Histogram(\n",
    "        x = dates,\n",
    "        y = [for y in 1 .. count -> generator.Next(0, 200)],\n",
    "        name = \"Open\",\n",
    "        histfunc = \"sum\")\n",
    "    \n",
    "let closeByTime =\n",
    "    Histogram(\n",
    "        x = dates,\n",
    "        y = [for y in 1 .. count -> generator.Next(0, 200)],\n",
    "        name = \"Close\",\n",
    "        histfunc = \"sum\")\n",
    "    \n",
    "[openByTime; closeByTime]\n",
    "|> Chart.Plot"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Area chart and Polar Area chart"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "By populating hte property `fill` of a `Scatter` trace the chart will render as area chart.\n",
    "\n",
    "Here is set to `\"tozeroy\"` which will create a fill zone underneath the line reachin to the 0 of the y axis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let openSeries =\n",
    "    Scatter(\n",
    "        name = \"Open\",\n",
    "        x = [1; 2; 3; 4],\n",
    "        y = [10; 15; 13; 17],\n",
    "        fill = \"tozeroy\",\n",
    "        mode= \"lines\")\n",
    "    \n",
    "let closeSeries =\n",
    "    Scatter(\n",
    "        name = \"Close\",\n",
    "        x = [1; 2; 3; 4],\n",
    "        y = [3; 5; 11; 9],\n",
    "        fill = \"tozeroy\",\n",
    "        mode= \"lines\")\n",
    "\n",
    "[openSeries; closeSeries]\n",
    "|> Chart.Plot\n",
    "|> Chart.WithTitle \"Open vs Close\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With one `fill` set to `\"tonexty\"` the cahrt will fill the aread between traces."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "openSeries.fill <- None\n",
    "closeSeries.fill <- \"tonexty\"\n",
    "\n",
    "[openSeries; closeSeries]\n",
    "|> Chart.Plot\n",
    "|> Chart.WithTitle \"Open vs Close\""
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Using `Area` traces we can generate radial area chart. In this example we are using cardinal points to xpress angular values.\n",
    "The list `[\"North\"; \"N-E\"; \"East\"; \"S-E\"; \"South\"; \"S-W\"; \"West\"; \"N-W\"]` will be autoimatically translated to angular values."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "dotnet_interactive": {
     "language": "fsharp"
    }
   },
   "outputs": [],
   "source": [
    "let winDirections =  [\"North\"; \"N-E\"; \"East\"; \"S-E\"; \"South\"; \"S-W\"; \"West\"; \"N-W\"]\n",
    "\n",
    "let areaTrace1 =\n",
    "    Area(\n",
    "        r = [77.5; 72.5; 70.0; 45.0; 22.5; 42.5; 40.0; 62.5],\n",
    "        t = winDirections,\n",
    "        name = \"11-14 m/s\",\n",
    "        marker = Marker(color = \"rgb(106,81,163)\"))\n",
    "\n",
    "let areaTrace2 =\n",
    "    Area(\n",
    "        r = [57.49999999999999; 50.0; 45.0; 35.0; 20.0; 22.5; 37.5; 55.00000000000001],\n",
    "        t = winDirections,\n",
    "        name = \"8-11 m/s\",\n",
    "        marker = Marker(color = \"rgb(158,154,200)\"))\n",
    "\n",
    "let areaTrace3 = \n",
    "    Area(\n",
    "        r = [40.0; 30.0; 30.0; 35.0; 7.5; 7.5; 32.5; 40.0],\n",
    "        t = winDirections,\n",
    "        name = \"5-8 m/s\",\n",
    "        marker = Marker(color = \"rgb(203,201,226)\"))\n",
    "\n",
    "let areaTrace4 =\n",
    "    Area(\n",
    "        r = [20.0; 7.5; 15.0; 22.5; 2.5; 2.5; 12.5; 22.5],\n",
    "        t = winDirections,\n",
    "        name = \"< 5 m/s\",\n",
    "        marker = Marker(color = \"rgb(242,240,247)\"))\n",
    "\n",
    "let areaLayout =\n",
    "    Layout(\n",
    "        title = \"Wind Speed Distribution in Laurel, NE\",\n",
    "        font = Font(size = 16.),\n",
    "        legend = Legend(font = Font(size = 16.)),\n",
    "        radialaxis = Radialaxis(ticksuffix = \"%\"),\n",
    "        orientation = 270.)\n",
    "    \n",
    "[areaTrace1; areaTrace2; areaTrace3; areaTrace4]\n",
    "|> Chart.Plot\n",
    "|> Chart.WithLayout areaLayout"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".NET (F#)",
   "language": "F#",
   "name": ".net-fsharp"
  },
  "language_info": {
   "file_extension": ".fs",
   "mimetype": "text/x-fsharp",
   "name": "C#",
   "pygments_lexer": "fsharp",
   "version": "4.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}